/*
 * HSM Proxy Project.
 * Copyright (C) 2013 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.hsm.jca;

import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

import be.fedict.hsm.client.HSMProxyClient;

/**
 * HSM Proxy implementation of {@link SignatureSpi}. The supported signature
 * algorithms are:
 * <ul>
 * <li>SHA1withRSA</li>
 * <li>SHA256withRSA</li>
 * <li>SHA512withRSA</li>
 * </ul>
 * 
 * @author Frank Cornelis
 * 
 */
public class HSMProxySignature extends SignatureSpi {

	private final static Map<String, String> signatureAlgoTodigestAlgos;

	private final MessageDigest messageDigest;

	private HSMProxyPrivateKey privateKey;

	static {
		signatureAlgoTodigestAlgos = new HashMap<String, String>();
		signatureAlgoTodigestAlgos.put("SHA1withRSA", "SHA-1");
		signatureAlgoTodigestAlgos.put("SHA224withRSA", "SHA-224");
		signatureAlgoTodigestAlgos.put("SHA256withRSA", "SHA-256");
		signatureAlgoTodigestAlgos.put("SHA384withRSA", "SHA-384");
		signatureAlgoTodigestAlgos.put("SHA512withRSA", "SHA-512");
	}

	public HSMProxySignature(String signatureAlgorithm)
			throws NoSuchAlgorithmException {
		if (false == signatureAlgoTodigestAlgos.containsKey(signatureAlgorithm)) {
			throw new NoSuchAlgorithmException(signatureAlgorithm);
		}
		String digestAlgo = signatureAlgoTodigestAlgos.get(signatureAlgorithm);
		this.messageDigest = MessageDigest.getInstance(digestAlgo);
	}

	@Override
	protected void engineInitVerify(PublicKey publicKey)
			throws InvalidKeyException {
	}

	@Override
	protected void engineInitSign(PrivateKey privateKey)
			throws InvalidKeyException {
		this.privateKey = (HSMProxyPrivateKey) privateKey;
		this.messageDigest.reset();
	}

	@Override
	protected void engineUpdate(byte b) throws SignatureException {
		this.messageDigest.update(b);
	}

	@Override
	protected void engineUpdate(byte[] b, int off, int len)
			throws SignatureException {
		this.messageDigest.update(b, off, len);
	}

	@Override
	protected byte[] engineSign() throws SignatureException {
		byte[] digestValue = this.messageDigest.digest();
		String keyAlias = this.privateKey.getAlias();
		HSMProxyKeyStoreParameter keyStoreParameter = this.privateKey
				.getKeyStoreParameter();
		String endpointAddress = keyStoreParameter.getHsmProxyEndpointAddress();
		X509Certificate credentialCertificate = keyStoreParameter
				.getCredentialCertificate();
		HSMProxyClient hsmProxyClient = keyStoreParameter.getHSMProxyClient();
		byte[] signatureValue = hsmProxyClient.sign(digestValue,
				this.messageDigest.getAlgorithm(), keyAlias);
		HSMProxyAudit hsmProxyAudit = keyStoreParameter.getHsmProxyAudit();
		if (null != hsmProxyAudit) {
			hsmProxyAudit.record(digestValue,
					this.messageDigest.getAlgorithm(), keyAlias,
					credentialCertificate, endpointAddress);
		}
		return signatureValue;
	}

	@Override
	protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
		return false;
	}

	@Override
	@Deprecated
	protected void engineSetParameter(String param, Object value)
			throws InvalidParameterException {
	}

	@Override
	@Deprecated
	protected Object engineGetParameter(String param)
			throws InvalidParameterException {
		return null;
	}
}
